
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>坐标点可视化工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            height: 100vh;
            display: flex;
            color: #333;
        }

        .container {
            display: flex;
            width: 100%;
            height: 100vh;
            background: white;
            box-shadow: 0 0 20px rgba(0,0,0,0.1);
        }

        .sidebar {
            width: 300px;
            background: #f8f9fa;
            padding: 20px;
            border-right: 1px solid #e9ecef;
            overflow-y: auto;
        }

        .main-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            padding: 20px;
        }

        .controls {
            margin-bottom: 20px;
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }

        .btn {
            padding: 10px 15px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.3s ease;
        }

        .btn-primary {
            background: #007bff;
            color: white;
        }

        .btn-primary:hover {
            background: #0056b3;
        }

        .btn-danger {
            background: #dc3545;
            color: white;
        }

        .btn-danger:hover {
            background: #c82333;
        }

        .btn-success {
            background: #28a745;
            color: white;
        }

        .btn-success:hover {
            background: #1e7e34;
        }

        .canvas-container {
            flex: 1;
            border: 2px solid #dee2e6;
            border-radius: 10px;
            position: relative;
            background: white;
            overflow: hidden;
        }

        #coordinateCanvas {
            cursor: crosshair;
            display: block;
        }

        .point-list {
            list-style: none;
        }

        .point-item {
            background: white;
            margin-bottom: 10px;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .point-info {
            flex: 1;
        }

        .point-coords {
            font-weight: bold;
            color: #007bff;
            margin-bottom: 5px;
        }

        .point-actions {
            display: flex;
            gap: 5px;
        }

        .btn-small {
            padding: 5px 8px;
            font-size: 12px;
        }

        .input-group {
            display: flex;
            gap: 5px;
            margin-top: 5px;
        }

        .input-group input {
            width: 60px;
            padding: 5px;
            border: 1px solid #ced4da;
            border-radius: 3px;
            font-size: 12px;
        }

        .stats {
            background: #e3f2fd;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
            text-align: center;
        }

        .stats h3 {
            color: #1976d2;
            margin-bottom: 10px;
        }

        .instructions {
            background: #fff3cd;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
            border-left: 4px solid #ffc107;
        }

        .instructions h4 {
            color: #856404;
            margin-bottom: 10px;
        }

        .instructions ul {
            margin-left: 20px;
            color: #856404;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="sidebar">
            <div class="instructions">
                <h4>使用说明</h4>
                <ul>
                    <li>点击画布添加坐标点</li>
                    <li>在右侧列表中编辑或删除点</li>
                    <li>支持导出坐标数据</li>
                </ul>
            </div>

            <div class="stats">
                <h3>坐标点统计</h3>
                <div>总数: <span id="pointCount">0</span></div>
            </div>

            <div class="controls">
                <button class="btn btn-danger" onclick="clearAllPoints()">清空所有</button>
                <button class="btn btn-success" onclick="exportPoints()">导出 JSON</button>
            </div>

            <h3 style="margin-bottom: 15px; color: #495057;">坐标点列表</h3>
            <ul id="pointList" class="point-list"></ul>
        </div>

        <div class="main-content">
            <div class="controls">
                <h2 style="color: #495057; margin-bottom: 10px;">坐标点可视化工具</h2>
            </div>
            <div class="canvas-container">
                <canvas id="coordinateCanvas"></canvas>
            </div>
        </div>
    </div>

    <script>
        class PointVisualizer {
            constructor() {
                this.canvas = document.getElementById('coordinateCanvas');
                this.ctx = this.canvas.getContext('2d');
                this.points = [];
                this.scale = 1;
                this.offsetX = 0;
                this.offsetY = 0;
                
                this.setupCanvas();
                this.bindEvents();
                this.loadPoints();
            }

            setupCanvas() {
                const container = this.canvas.parentElement;
                this.canvas.width = container.clientWidth;
                this.canvas.height = container.clientHeight;
                
                // 设置坐标系原点
                this.offsetX = this.canvas.width / 2;
                this.offsetY = this.canvas.height / 2;
                
                this.drawGrid();
            }

            bindEvents() {
                this.canvas.addEventListener('click', (e) => this.handleCanvasClick(e));
                window.addEventListener('resize', () => this.setupCanvas());
            }

            async loadPoints() {
                try {
                    const response = await fetch('/api/points');
                    const data = await response.json();
                    this.points = data;
                    this.updateDisplay();
                } catch (error) {
                    console.error('加载坐标点失败:', error);
                }
            }

            async handleCanvasClick(e) {
                const rect = this.canvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                
                // 转换为坐标系坐标
                const coordX = (x - this.offsetX) / 20;
                const coordY = -(y - this.offsetY) / 20;
                
                await this.addPoint(coordX, coordY);
            }

            async addPoint(x, y) {
                try {
                    const response = await fetch('/api/points', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({ x: x, y: y }),
                    });
                    
                    const result = await response.json();
                    if (result.success) {
                        this.points.push(result.point);
                        this.updateDisplay();
                    }
                } catch (error) {
                    console.error('添加坐标点失败:', error);
                }
            }

            async deletePoint(index) {
                try {
                    const response = await fetch(`/api/points/${index}`, {
                        method: 'DELETE',
                    });
                    
                    const result = await response.json();
                    if (result.success) {
                        this.points.splice(index, 1);
                        this.updateDisplay();
                    }
                } catch (error) {
                    console.error('删除坐标点失败:', error);
                }
            }

            async updatePoint(index, x, y) {
                try {
                    const response = await fetch(`/api/points/${index}`, {
                        method: 'PUT',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({ x: x, y: y }),
                    });
                    
                    const result = await response.json();
                    if (result.success) {
                        this.points[index] = result.point;
                        this.updateDisplay();
                    }
                } catch (error) {
                    console.error('更新坐标点失败:', error);
                }
            }

            async clearAllPoints() {
                try {
                    const response = await fetch('/api/points/clear', {
                        method: 'POST',
                    });
                    
                    const result = await response.json();
                    if (result.success) {
                        this.points = [];
                        this.updateDisplay();
                    }
                } catch (error) {
                    console.error('清空坐标点失败:', error);
                }
            }

            async exportPoints() {
                try {
                    const response = await fetch('/api/points/export');
                    const data = await response.json();
                    
                    const blob = new Blob([JSON.stringify(data, null, 2)], {
                        type: 'application/json',
                    });
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = 'coordinates.json';
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                } catch (error) {
                    console.error('导出坐标点失败:', error);
                }
            }

            drawGrid() {
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                
                // 绘制网格
                this.ctx.strokeStyle = '#e0e0e0';
                this.ctx.lineWidth = 1;
                
                const gridSize = 20;
                
                // 垂直线
                for (let x = this.offsetX % gridSize; x < this.canvas.width; x += gridSize) {
                    this.ctx.beginPath();
                    this.ctx.moveTo(x, 0);
                    this.ctx.lineTo(x, this.canvas.height);
                    this.ctx.stroke();
                }
                
                // 水平线
                for (let y = this.offsetY % gridSize; y < this.canvas.height; y += gridSize) {
                    this.ctx.beginPath();
                    this.ctx.moveTo(0, y);
                    this.ctx.lineTo(this.canvas.width, y);
                    this.ctx.stroke();
                }
                
                // 绘制坐标轴
                this.ctx.strokeStyle = '#333';
                this.ctx.lineWidth = 2;
                
                // X轴
                this.ctx.beginPath();
                this.ctx.moveTo(0, this.offsetY);
                this.ctx.lineTo(this.canvas.width, this.offsetY);
                this.ctx.stroke();
                
                // Y轴
                this.ctx.beginPath();
                this.ctx.moveTo(this.offsetX, 0);
                this.ctx.lineTo(this.offsetX, this.canvas.height);
                this.ctx.stroke();
                
                // 绘制刻度
                this.ctx.fillStyle = '#666';
                this.ctx.font = '12px Arial';
                this.ctx.textAlign = 'center';
                
                // X轴刻度
                for (let i = -10; i <= 10; i++) {
                    if (i !== 0) {
                        const x = this.offsetX + i * gridSize;
                        this.ctx.fillText(i.toString(), x, this.offsetY + 15);
                    }
                }
                
                // Y轴刻度
                this.ctx.textAlign = 'right';
                for (let i = -10; i <= 10; i++) {
                    if (i !== 0) {
                        const y = this.offsetY - i * gridSize;
                        this.ctx.fillText(i.toString(), this.offsetX - 5, y + 4);
                    }
                }
            }

            drawPoints() {
                this.drawGrid();
                
                this.points.forEach((point, index) => {
                    const x = this.offsetX + point.x * 20;
                    const y = this.offsetY - point.y * 20;
                    
                    // 绘制点
                    this.ctx.fillStyle = '#ff4444';
                    this.ctx.beginPath();
                    this.ctx.arc(x, y, 6, 0, 2 * Math.PI);
                    this.ctx.fill();
                    
                    // 绘制坐标标签
                    this.ctx.fillStyle = '#333';
                    this.ctx.font = '12px Arial';
                    this.ctx.textAlign = 'left';
                    this.ctx.fillText(`(${point.x}, ${point.y})`, x + 10, y - 10);
                });
            }

            updateDisplay() {
                this.drawPoints();
                this.updatePointList();
                document.getElementById('pointCount').textContent = this.points.length;
            }

            updatePointList() {
                const listElement = document.getElementById('pointList');
                listElement.innerHTML = '';
                
                this.points.forEach((point, index) => {
                    const li = document.createElement('li');
                    li.className = 'point-item';
                    li.innerHTML = `
                        <div class="point-info">
                            <div class="point-coords">点 ${index + 1}: (${point.x}, ${point.y})</div>
                            <div class="input-group">
                                <input type="number" id="x-${index}" value="${point.x}" step="0.1" style="width: 50px;">
                                <input type="number" id="y-${index}" value="${point.y}" step="0.1" style="width: 50px;">
                                <button class="btn btn-primary btn-small" onclick="visualizer.updatePointFromInput(${index})">更新</button>
                            </div>
                        </div>
                        <div class="point-actions">
                            <button class="btn btn-danger btn-small" onclick="visualizer.deletePoint(${index})">删除</button>
                        </div>
                    `;
                    listElement.appendChild(li);
                });
            }

            updatePointFromInput(index) {
                const x = parseFloat(document.getElementById(`x-${index}`).value);
                const y = parseFloat(document.getElementById(`y-${index}`).value);
                this.updatePoint(index, x, y);
            }
        }

        // 初始化可视化器
        const visualizer = new PointVisualizer();

        // 全局函数
        function clearAllPoints() {
            if (confirm('确定要清空所有坐标点吗？')) {
                visualizer.clearAllPoints();
            }
        }

        function exportPoints() {
            visualizer.exportPoints();
        }
    </script>
</body>
</html>
